/**
 * 
 */
package edu.umd.clip.lm.model.training;


import java.io.File;

import com.sleepycat.bind.tuple.*;
import com.sleepycat.collections.StoredMap;
import com.sleepycat.je.*;

import edu.umd.clip.lm.util.CountDistribution;
import edu.umd.clip.lm.util.CountDistributionBinding;


/**
 * @author Denis Filimonov <den@cs.umd.edu>
 *
 */
public class BDBSmoothingDataStorage extends SmoothingDataStorage {
	private Environment env;
	private Database db;
	private StoredMap<Key,CountDistribution> theMap;
	public static final String SMOOTHING_DB_NAME = "smoothing";
	
	public static int MAX_LOG_FILE_SIZE = 100*1024*1024;
	
	public BDBSmoothingDataStorage(Environment env, boolean clear) throws DatabaseException {
		this.env = env;
		init(clear);
	}
	
	public BDBSmoothingDataStorage(String path, boolean clear) throws DatabaseException {
		EnvironmentConfig envConf = new EnvironmentConfig();
		envConf.setAllowCreate(true);
		envConf.setSharedCache(true);
		envConf.setTransactional(false);
		envConf.setReadOnly(false);
		envConf.setCachePercent(20);
		envConf.setConfigParam("je.log.fileMax", Integer.toString(MAX_LOG_FILE_SIZE));
		
		File dir = new File(path);
		if (!dir.isDirectory()) {
			dir.mkdirs();
		}
		env = new Environment(dir, envConf);

		init(clear);
	}
	
	private void init(boolean clear) throws DatabaseException {
		DatabaseConfig dbConf = new DatabaseConfig();
		dbConf.setReadOnly(false);
		dbConf.setAllowCreate(true);
		dbConf.setTransactional(false);
		dbConf.setDeferredWrite(true);

		if (clear) {
			try {
				env.truncateDatabase(null, SMOOTHING_DB_NAME, false);
			} catch(DatabaseException e) {}
		}
		
		db = env.openDatabase(null, SMOOTHING_DB_NAME, dbConf);
		
		theMap = new StoredMap<Key,CountDistribution>(db, KeyBinding.instance, CountDistributionBinding.instance, true);
	}
	
	public void close() throws DatabaseException {
		db.close();
		env.close();
	}
	
	private static class KeyBinding extends TupleBinding<Key> {
		private static final KeyBinding instance = new KeyBinding();
		
		private KeyBinding() {}
		/* (non-Javadoc)
		 * @see com.sleepycat.bind.tuple.TupleBinding#entryToObject(com.sleepycat.bind.tuple.TupleInput)
		 */
		@Override
		public Key entryToObject(TupleInput input) {
			int clusterid = input.readInt();
			byte dataid = input.readByte();
			return new Key(clusterid, dataid);
		}

		/* (non-Javadoc)
		 * @see com.sleepycat.bind.tuple.TupleBinding#objectToEntry(java.lang.Object, com.sleepycat.bind.tuple.TupleOutput)
		 */
		@Override
		public void objectToEntry(Key object, TupleOutput output) {
			output.writeInt(object.clusterid);
			output.writeByte(object.dataid);
		}
	}
	
	/* (non-Javadoc)
	 * @see edu.umd.clip.lm.model.training.SmoothingDataStorage#getDistribution(edu.umd.clip.lm.model.training.SmoothingDataStorage.Key)
	 */
	@Override
	public CountDistribution getDistribution(Key key) {
		return theMap.get(key);
	}

	/* (non-Javadoc)
	 * @see edu.umd.clip.lm.model.training.SmoothingDataStorage#putDistribution(edu.umd.clip.lm.model.training.SmoothingDataStorage.Key, edu.umd.clip.lm.model.training.SmoothingDataStorage.Distribution)
	 */
	@Override
	public void putDistribution(Key key, CountDistribution dist) {
		theMap.put(key, dist);
	}

	/* (non-Javadoc)
	 * @see edu.umd.clip.lm.model.training.SmoothingDataStorage#sync()
	 */
	@Override
	public void sync() {
		try {
			db.sync();
		} catch (DatabaseException e) {
			e.printStackTrace();
		}
	}

}
